package com.zjj.algorithm.learning.leetcode.matrix;

/**
 * 79. 单词搜索 中档题
 * 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中，返回 true ；否则，返回 false 。
 * <p>
 * 单词必须按照字母顺序，通过相邻的单元格内的字母构成，其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
 * <p>
 * <p>
 * <p>
 * 输入：board =
 * [
 * ["A","B","C","E"],
 * ["S","F","C","S"],
 * ["A","D","E","E"]
 * ],
 * word = "ABCCED"
 * 输出：true
 * <p>
 * 输入：board =
 * [
 * ["A","B","C","E"],
 * ["S","F","C","S"],
 * ["A","D","E","E"]
 * ], word = "ABCB"
 * 输出：false
 * 提示：
 * <p>
 * m == board.length
 * n = board[i].length
 * 1 <= m, n <= 6
 * 1 <= word.length <= 15
 * board 和 word 仅由大小写英文字母组成
 *
 * @author zjj_admin
 * @date 2022/12/8 13:57
 */
public class WordSearch {

    public static void main(String[] args) {

        char[][] board = new char[][]{
                {'A', 'B', 'C', 'E'},
                {'S', 'F', 'C', 'S'},
                {'A', 'D', 'E', 'E'}
        };

//        boolean res = exist2(board, "SEE");
//        System.out.println("res = " + res);

        WordSearch wordSearch = new WordSearch();
        boolean res = wordSearch.exist2(board, "SEE");
        System.out.println("res = " + res);

    }

    private final int[][] DIRECTIONS = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}};
    private int rows;
    private int cols;
    private int len;
    private boolean[][] visited;
    private char[] charArray;
    private char[][] board;

    /**
     * 回溯
     * <p>
     * 时间
     * 98 ms
     * 击败
     * 57.92%
     * 内存
     * 39.4 MB
     * 击败
     * 76.93%
     *
     * @param board
     * @param word
     * @return
     */
    public boolean exist2(char[][] board, String word) {
        rows = board.length;
        if (rows == 0) {
            return false;
        }
        cols = board[0].length;
        visited = new boolean[rows][cols];

        len = word.length();
        charArray = word.toCharArray();
        this.board = board;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                //从第一个坐标开始查找
                if (dfs(i, j, 0)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean dfs(int x, int y, int begin) {
        if (begin == len - 1) {
            return board[x][y] == charArray[begin];
        }
        if (board[x][y] == charArray[begin]) {
            //当当前的坐标和字母相同时，将当前坐标设置为已经访问
            visited[x][y] = true;
            //依次访问当前坐标的前后左右
            for (int[] direction : DIRECTIONS) {

                int newX = x + direction[0];
                int newY = y + direction[1];
                if (inArea(newX, newY) && !visited[newX][newY]) {
                    if (dfs(newX, newY, begin + 1)) {
                        return true;
                    }
                }
            }
            //若当前坐标的前后左右都不行是，就将当前坐标设置为未访问状态
            visited[x][y] = false;
        }
        return false;
    }

    private boolean inArea(int x, int y) {
        return x >= 0 && x < rows && y >= 0 && y < cols;
    }


    /**
     * 目前的结果只能搜索一次，不能回溯搜索
     *
     * @param board
     * @param word
     * @return
     */
    public static boolean exist(char[][] board, String word) {

        int len = word.length();
        char c;
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[i].length; j++) {
                int index = 0;
                c = word.charAt(index);
                if (c == board[i][j]) {
                    index++;
                    int i1 = i, j1 = j;
                    while (index < len) {
                        c = word.charAt(index);
                        if (i1 - 1 >= 0 && board[i1 - 1][j1] == c) {
                            i1--;
                            if (++index >= len) {
                                return true;
                            }
                            continue;
                        }
                        if (i1 + 1 < board.length && board[i1 + 1][j1] == c) {
                            i1++;
                            if (++index >= len) {
                                return true;
                            }
                            continue;
                        }
                        if (j1 - 1 >= 0 && board[i1][j1 - 1] == c) {
                            j1--;
                            if (++index >= len) {
                                return true;
                            }
                            continue;
                        }
                        if (j1 + 1 < board[0].length && board[i1][j1 + 1] == c) {
                            j1++;
                            if (++index >= len) {
                                return true;
                            }
                            continue;
                        }
                        break;
                    }
                }

            }
        }
        return false;
    }
}
